`timescale 1ns / 1ps

module fc_3(
    input  wire         clk_i,
    input  wire         rst_n_i,
    input  wire         start_i,
    output wire         done_o,

    input wire [1535:0] input_data_i, // 输入数据, 32*48

    output reg          weight_addr_o,
    input wire [255:0]  weight_i,     // 权重,

    output reg           bias_addr_o,
    input  wire [7:0]    bias_i,                // 8-bit signed    

    output wire [1535:0] mul_data1_o, 
    output wire [255:0]  mul_data2_o, // 权重数据, 32*8
    input  wire [1791:0] mul_result_i, // 

    output reg signed [63:0]  fc_output_data_o,
    output reg         fc_output_wren_o,
    output reg          fc_output_addr_o

    );

    parameter INPUT_DIM    = 32;
    parameter OUTPUT_DIM   = 2;
    parameter MULT_LATENCY = 3;
    parameter RAM_LATENCY  = 3;
    localparam ADDER_LATENCY = 3;

    localparam INDEX_ADDR      = 0;                             // 0
    localparam INDEX_RAM_OUT   = INDEX_ADDR + RAM_LATENCY;      // 3
    localparam INDEX_MUL_OUT   = INDEX_RAM_OUT + MULT_LATENCY;   // 6
    localparam INDEX_ADDER_OUT = INDEX_MUL_OUT + ADDER_LATENCY;  // 9
    localparam INDEX_BIAS      = INDEX_ADDER_OUT + 1;           // 10
    localparam INDEX_WRITE     = INDEX_ADDER_OUT + 1;           // 11   
    localparam INDEX_DONE      = INDEX_WRITE + 2;               // 13
    localparam BIAS_PIPE_OFFSET = (INDEX_BIAS > RAM_LATENCY) ? (INDEX_BIAS - RAM_LATENCY - 1) : 0;


    reg out_channel;  // 1位宽，支持2个输出通道（0,1）
    reg running;
    reg out_channel_dly [0:24];  // 对应修改延迟链位宽为1位
    reg [7:0] bias_reg [0:24];

    wire signed [63:0] final_sum;

    reg signed [63:0] accumulator;

    reg [INDEX_DONE:0] done_shift;

    integer i;
    always @(posedge clk_i or negedge rst_n_i) begin
        if (!rst_n_i) begin
            running <= 0;
            out_channel <= 0;
        end else if (start_i) begin
            running <= 1;
        end else if (done_shift[INDEX_DONE]) begin
            running <= 0;
        end
    end

    always @(posedge clk_i or negedge rst_n_i) begin
        if (!rst_n_i) begin
            out_channel <= 0;
        end else if (running) begin
            if (out_channel == OUTPUT_DIM-1) begin
                out_channel <= out_channel;  // 保持在最后一个值
            end else begin
                out_channel <= out_channel + 1;
            end
        end else begin
            out_channel <= 0;  // 不运行时重置
        end
    end


    integer d;
    always @(posedge clk_i or negedge rst_n_i) begin
        if (!rst_n_i) begin
            for(d=0; d<=24; d=d+1) begin
                out_channel_dly[d] <= 0;
            end
        end else begin
            out_channel_dly[0] <= out_channel;
            for (i=1; i<=24; i=i+1) begin
                out_channel_dly[i] <= out_channel_dly[i-1]; 
            end
        end
    end

    // 阶段1：地址生成
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            weight_addr_o     <= 0;
            bias_addr_o       <= 0;
        end else if(running) begin
            weight_addr_o     <= out_channel;
            bias_addr_o       <= out_channel;
        end
    end

    // 阶段2-4：RAM 输出寄存对齐 (3拍)
    // 偏置延迟链：bias_i 已包含 BRAM 3 拍延迟，这里再移位用于对齐到 INDEX_BIAS
    integer b;
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            for(b=0;b<=24;b=b+1) bias_reg[b] <= 0;
        end else begin
            bias_reg[0] <= bias_i;
            for(b=1;b<=24;b=b+1)
                bias_reg[b] <= bias_reg[b-1];
        end
    end

    assign mul_data1_o = input_data_i;
    assign mul_data2_o = weight_i;

    adder_tree_3 adder_inst (
        .clk(clk_i),
        .rst_n(rst_n_i),
        .data(mul_result_i),
        .sum_out(final_sum)
    );

    // 阶段12：加偏置 (INDEX_BIAS) 仅在最后一组
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            accumulator <= 0;
        end else begin
            accumulator <= final_sum +
                               {{56{bias_reg[BIAS_PIPE_OFFSET][7]}}, bias_reg[BIAS_PIPE_OFFSET]};
        end
    end

    always @(posedge clk_i or negedge rst_n_i) begin
        if (!rst_n_i) begin
            fc_output_wren_o <= 0;
            fc_output_data_o <= 0;
            fc_output_addr_o <= 0;
        end else if (running) begin
            // 只在正确的时序阶段写入输出
            if (out_channel_dly[INDEX_WRITE] == OUTPUT_DIM - 1) begin
                fc_output_wren_o <= 1;
                fc_output_addr_o <= out_channel_dly[INDEX_WRITE];
                fc_output_data_o <= accumulator;
            end else begin
                fc_output_wren_o <= 1'b0;
            end
        end
    end


    // 阶段15：done 产生：last_launch -> 在 INDEX_DONE 周期输出
    wire last_launch = running &&
                       (out_channel == OUTPUT_DIM-1);

    integer ds;
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            done_shift <= 0;
        end else begin
            done_shift <= {done_shift[INDEX_DONE-1:0], last_launch};
        end
    end

    assign done_o = done_shift[INDEX_DONE];

endmodule
